#include "headers/linked_list.h"
extern volatile BOOL critical_section;

linked_list_type* create_linked_list(BOOL looped)
{
	BOOL backup = critical_section;
	critical_section = true;
	linked_list_type* list;
	setLastError(NO_ERROR);
	list = (linked_list_type*)kmalloc(sizeof(linked_list_type));
	list->first = 0;
	list->last = 0;
	list->count = 0;
	list->looped = looped;
	list->current = 0;
	critical_section = backup;
	return list;
}
void destroy_linked_list(linked_list_type* list)
{
	BOOL backup = critical_section;
	critical_section = true;
	u32int i = 0;
	linked_list_element* current = list->first;
	linked_list_element* tmp;
	setLastError(NO_ERROR);
	for(i = 0; (i < list->count) && (current != NULL); i++)
	{
		tmp = current->next;
		kfree(current);
		current = tmp;
	}
}

void add_to_list_end(u32int data, linked_list_type* list)
{
	BOOL backup = critical_section;
	critical_section = true;
	setLastError(NO_ERROR);
	if(list->count == 0)
	{
		list->last = list->first = list->current = (linked_list_element*) kmalloc(sizeof(linked_list_element));	
	}
	else
	{
		list->last = list->last->next = (linked_list_element*) kmalloc(sizeof(linked_list_element));
	}
	list->last->data = data;
	list->last->next = list->looped?list->first:0;
	list->count++;
	critical_section = backup;
}

u32int get_list_element(u32int index, linked_list_type* list)
{
	u32int i = 0;
	linked_list_element* current = list->first;
	setLastError(NO_ERROR);
	
	if(list->count == 0)
	{
		setLastError(ERR_IS_EMPTY);
		return LIST_ERROR;
	}
	
	if(index > (list->count - 1))
	{
		setLastError(ERR_OUT_OF_BOUNDS);
		return LIST_ERROR;
	}
	
	if(index == (list->count - 1))
		return list->last->data;
		
	for(i = 0; (i < index) && (i < list->count); i++)
		current = current->next;
	return current->data;
}
u32int get_next_from_list(BOOL* end, linked_list_type* list)
{
	linked_list_element* tmp = list->current;
	setLastError(NO_ERROR);
	if(list->count == 0)
	{
		setLastError(ERR_IS_EMPTY);
		return LIST_ERROR;
	}
	
	if(end)
		*end = false;
	if(list->current == list->last)
	{
		if(end)
			*end = true;
		list->current = list->first;
	}
	else
		list->current = list->current->next;
	return tmp->data;
	
}
//Returns 0 if successfull, LIST_ERROR - otherwise
u32int remove_from_list(u32int index, linked_list_type* list)
{
	u32int i = 0;
	linked_list_element* current = list->first;
	linked_list_element* tmp;
	
	BOOL backup = critical_section;
	critical_section = true;
	
	setLastError(NO_ERROR);
	
	if(index > (list->count - 1))
	{
		setLastError(ERR_IS_EMPTY);
		critical_section = backup;
		return LIST_ERROR;
	}
	
	if((list->count == 0) || (index > (list->count - 1)))
	{
		setLastError(ERR_OUT_OF_BOUNDS);
		critical_section = backup;
		return LIST_ERROR;
	}
		
	if(index == 0)
	{
		tmp = list->first;
		
		if(list->current == tmp)
			get_next_from_list(NULL, list);
		
		list->first = list->first->next;
		if(list->looped)
			list->last->next = list->first;
	}
	else
	{
		for(i = 0; (i < list->count) && (i < index-1) && (current->next != NULL); i++)
			current = current->next;
		
		tmp = current->next;
		if(list->current == tmp)
			get_next_from_list(NULL, list);
		if(current->next == list->last)
			list->last = current;
		current->next = current->next->next;
		
	}
	
	list->count--;
	kfree(tmp);
	critical_section = backup;
	return 0;
}

u32int get_last_from_list(linked_list_type* list)
{
	return list->last->data;
}

u32int get_first_from_list(linked_list_type* list)
{
	return list->first->data;
}

u32int get_number_of_list_elements(linked_list_type* list)
{
	return list->count;
}

//Return LIST_ERROR if not found.
u32int find_in_list(u32int data, linked_list_type* list)
{
	linked_list_element* current = list->first;
	u32int index = 0;
	BOOL end = false;
	setLastError(NO_ERROR);
	while(current?((current->data != data) && !end):0)
	{
		index++;
		current = current->next;
		if(current == list->first) //In case of looped list
			end = true;
	}
	
	if(!current || end)
	{
		setLastError(ERR_ELEMENT_NOT_FOUND);
		return LIST_ERROR;
	}
	return index;
}